Skip to content

Feature/grid filter continued#269

Open
santipalenque wants to merge 5 commits into
mainfrom
feature/grid-filter-continued
Open

Feature/grid filter continued#269
santipalenque wants to merge 5 commits into
mainfrom
feature/grid-filter-continued

Conversation

@santipalenque

@santipalenque santipalenque commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

https://app.clickup.com/t/86baf0qnm

Summary by CodeRabbit

Release Notes

  • New Features
    • Grid filter adds before/after operators.
    • Grid filter supports new value types: datetime, number (min/max + integer handling), and async selects for speaker and company.
    • useGridFilter now exposes setFilters(filters, joinOperator) to programmatically populate filter state.
    • Filter UI layout improved for clearer spacing and responsive sizing.
  • Bug Fixes
    • Async select criteria now emits a console error when a required custom parser is missing (to prevent incorrect serialization).
  • Documentation
    • Updated Grid Filter docs and placeholder/operator labels for the new types.
  • Tests
    • Expanded test coverage for new value types, serialization, and async behavior.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Extends the GridFilter component with five new value input types: datetime (MUI DateTimePicker storing Unix timestamps), number (clamped TextField), and async select types (asyncSelect, speaker, company) backed by a debounced AsyncSelectInput. Adds BEFORE/AFTER operators, introduces useGridFilter().setFilters() for external filter state management, enforces customParser for async types in parseFilter, refactors Filter layout, and documents and tests all new behavior.

Changes

GridFilter new value types, async select, and setFilters API

Layer / File(s) Summary
Operators, async type constants, and i18n strings
src/components/mui/GridFilter/utils.js, src/i18n/en.json
OPERATORS gains BEFORE (<=) and AFTER (>=) entries; ASYNC_VALUE_TYPES constant (["asyncSelect", "speaker", "company"]) is exported; i18n adds before/after operator labels and placeholder text for number, date, speaker, company, and async.
AsyncSelectInput: debounced async Autocomplete
src/components/mui/GridFilter/components/ValueInput/AsyncSelectInput.jsx
New component managing options/loading state with debounced queryFunction calls, mount-time empty-term fetch, unmount cleanup, single/multiple value normalization, and MUI Autocomplete render with CircularProgress adornment.
SpeakerSelectInput and CompanySelectInput wrappers
src/components/mui/GridFilter/components/ValueInput/SpeakerSelectInput.jsx, src/components/mui/GridFilter/components/ValueInput/CompanySelectInput.jsx
Thin wrappers over AsyncSelectInput wiring domain-specific queryFunction defaults (querySpeakers scoped by summitId, queryCompanies), localized placeholders, and formatOption mappings.
DateTimeInput: mode-driven MUI DateTimePicker
src/components/mui/GridFilter/components/ValueInput/DateTimeInput.jsx
New component mapping mode to picker views and formats, converting Unix timestamps to/from Moment instances, and emitting onChange with Unix value or null.
NumberInput: clamped integer/float TextField
src/components/mui/GridFilter/components/ValueInput/NumberInput.jsx
New component wrapping MUI TextField with float/int parsing, empty-to-null coercion, min/max clamping enforced on change, and keystroke blocking for scientific notation and decimals in integer mode.
ValueInput routing and Dropdown/Filter UI wiring
src/components/mui/GridFilter/components/ValueInput/index.jsx, src/components/mui/Dropdown/index.jsx, src/components/mui/GridFilter/components/Filter.jsx
ValueInput expands INPUT_TYPE_MAP to route new types, relaxes type to optional, adds object to value prop types. Dropdown removes .isRequired on options element shape and adds options: null default. Filter refactors layout with flex Box wrappers and removes hardcoded placeholder from ValueInput.
parseFilter async type validation
src/components/mui/GridFilter/GridFilter.jsx
parseFilter now resolves the matched criteria, emits console.error when an async type criterion lacks customParser, and preserves the existing customParser(filter) return path.
useGridFilter setFilters public API
src/components/mui/GridFilter/hooks/useGridFilter.jsx
Hook now exposes setFilters(filters, joinOperator) method that dispatches saveFilters to allow host applications to programmatically populate filter state in Redux store without invoking onApply.
GridFilter readme: new value types, async select, and setFilters API docs
src/components/mui/GridFilter/readme.md
Documents setFilters as an external API with required filter array shape and parsed values. Documents datetime (Unix timestamp storage independent of mode), number (clamped Number values with min/max/integer support), asyncSelect/speaker/company (option object shape with {value, label, raw}), customParser requirement, and provides example filter string construction for async types.
Test suite: value types, serialization, setFilters, and async contracts
src/components/mui/__tests__/GridFilter.test.jsx, src/components/mui/__tests__/useGridFilter.test.jsx
Mocks querySpeakers/queryCompanies and DateTimePicker; adds Filter suites for all new value types; extends serialization tests for datetime and number parsed output; adds async customParser contract tests; adds setFilters dispatch and default-behavior tests.

Sequence Diagram(s)

sequenceDiagram
  participant Filter
  participant ValueInput
  participant AsyncSelectInput
  participant queryFunction
  participant GridFilter_parseFilter

  Filter->>ValueInput: render(type="company", value, options)
  ValueInput->>AsyncSelectInput: delegate via INPUT_TYPE_MAP
  AsyncSelectInput->>queryFunction: queryFunction("", callback) on mount
  queryFunction-->>AsyncSelectInput: callback(companies)
  AsyncSelectInput->>Filter: onChange({target:{value:{value,label,raw}}})
  Filter->>GridFilter_parseFilter: handleSubmit(filters)
  GridFilter_parseFilter->>GridFilter_parseFilter: criteria=criterias[filter.criteria]
  GridFilter_parseFilter->>GridFilter_parseFilter: check ASYNC_VALUE_TYPES.includes(criteria.type)
  alt customParser missing
    GridFilter_parseFilter->>GridFilter_parseFilter: console.error
  else customParser provided
    GridFilter_parseFilter->>GridFilter_parseFilter: return customParser(filter)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • OpenStackweb/openstack-uicore-foundation#242: Both PRs directly touch src/components/mui/Dropdown/index.jsx, where the retrieved PR introduced the original Dropdown component and this PR extends it with optional options prop and null default.

Suggested reviewers

  • smarcet

Poem

🐇 Hop, hop, the filter grows tall,
Async speakers, companies, datetimes and all!
Numbers clamped and operators new,
BEFORE and AFTER join the queue.
A setFilters hook lets the host take control,
While customParser keeps each async goal whole! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and uses a non-descriptive generic phrase that does not convey meaningful information about the changeset. Replace with a more specific title that describes the main feature, such as: 'Add datetime, number, and async select inputs to GridFilter' or 'Implement setFilters capability and new input types for grid filtering'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/grid-filter-continued

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/mui/Dropdown/index.jsx (1)

42-63: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add optional chaining to prevent crash when options is null.

Now that options can be null (per the new defaultProps at line 99), line 52 and line 59 are missing the optional chaining guard that line 65 already has.

Crash scenario for line 52:

  1. Dropdown receives multiple={true} via the {...rest} spread (line 41)
  2. value is an array (e.g., [1, 2])
  3. options is null
  4. renderValue reaches line 50's Array.isArray(selected) check → true
  5. Line 52 executes options.map(...)runtime crash: "Cannot read property 'map' of null"

Crash scenario for line 59:

  1. Single-select mode (default)
  2. value is a non-empty scalar
  3. options is null
  4. Line 59 executes options.find(...)runtime crash
🛡️ Proposed fix
         if (Array.isArray(selected)) {
           const lookup = Object.fromEntries(
-            options.map((o) => [o.value, o.label])
+            options?.map((o) => [o.value, o.label]) || []
           );
           return selected
             .map((v) => lookup[v])
             .filter(Boolean)
             .join(", ");
         }
-        const selectedOption = options.find(
+        const selectedOption = options?.find(
           ({ value }) => value === selected
         );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/mui/Dropdown/index.jsx` around lines 42 - 63, The renderValue
function in the Dropdown component is missing optional chaining guards on the
options parameter. At line 52 where options.map is called to create the lookup
object for array values, and at line 59 where options.find is called to find the
selected option label, add optional chaining (the ?. operator) before the .map()
and .find() method calls respectively. This will prevent runtime crashes when
options is null by ensuring these methods are only called if options exists.
🧹 Nitpick comments (1)
src/components/mui/GridFilter/utils.js (1)

31-32: Operator value collision: affects semantic preservation on deserialization.

BEFORE and AFTER reuse the same operator values ("<=" and ">=") as LESS_OR_EQUAL and GREATER_OR_EQUAL. The parseFilter function serializes filters using only the operator value string (e.g., "fieldName<=value"), with no way to reconstruct which semantic intent was originally selected. On filter deserialization, a filter created with BEFORE becomes indistinguishable from one created with LESS_OR_EQUAL, losing temporal context.

If this aliasing is intentional (semantic labels for date-specific contexts), document the design. Otherwise, consider using distinct values (e.g., "<=" for numeric, "<@" for temporal) to preserve semantic information.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/mui/GridFilter/utils.js` around lines 31 - 32, The BEFORE and
AFTER operators in the grid filter operators object are using the same operator
values as LESS_OR_EQUAL and GREATER_OR_EQUAL respectively, causing operator
value collision. When filters are serialized and later deserialized through the
parseFilter function, there is no way to distinguish whether a filter was
originally created with BEFORE or LESS_OR_EQUAL (both use "<="), losing the
semantic temporal context. Change the operator values for BEFORE and AFTER to
use distinct values that do not collide with numeric comparison operators (for
example, use "<@" for BEFORE and ">@" for AFTER instead of reusing "<=" and
">=") to preserve semantic information during serialization and deserialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/mui/GridFilter/components/ValueInput/AsyncSelectInput.jsx`:
- Around line 56-74: The fetchOptions function has a race condition where slower
earlier requests can overwrite newer results when the user types quickly, and
callbacks can also update state after unmount. Add a ref to track the mounted
status and another ref to track the current request ID. In the useEffect, set
mounted to true and clear it in the cleanup function. Modify fetchOptions to
generate a unique request ID for each call and pass it to the queryFunction
callback. In the callback, before calling setOptions and setLoading, check that
the request ID matches the current request ID stored in the ref and that the
component is still mounted. This ensures only the latest active request mutates
state and prevents updates after unmount.

---

Outside diff comments:
In `@src/components/mui/Dropdown/index.jsx`:
- Around line 42-63: The renderValue function in the Dropdown component is
missing optional chaining guards on the options parameter. At line 52 where
options.map is called to create the lookup object for array values, and at line
59 where options.find is called to find the selected option label, add optional
chaining (the ?. operator) before the .map() and .find() method calls
respectively. This will prevent runtime crashes when options is null by ensuring
these methods are only called if options exists.

---

Nitpick comments:
In `@src/components/mui/GridFilter/utils.js`:
- Around line 31-32: The BEFORE and AFTER operators in the grid filter operators
object are using the same operator values as LESS_OR_EQUAL and GREATER_OR_EQUAL
respectively, causing operator value collision. When filters are serialized and
later deserialized through the parseFilter function, there is no way to
distinguish whether a filter was originally created with BEFORE or LESS_OR_EQUAL
(both use "<="), losing the semantic temporal context. Change the operator
values for BEFORE and AFTER to use distinct values that do not collide with
numeric comparison operators (for example, use "<@" for BEFORE and ">@" for
AFTER instead of reusing "<=" and ">=") to preserve semantic information during
serialization and deserialization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87ce6b08-1d5e-4fb1-b5ad-f9aefa59e15b

📥 Commits

Reviewing files that changed from the base of the PR and between 789b77e and df4fa23.

📒 Files selected for processing (13)
  • src/components/mui/Dropdown/index.jsx
  • src/components/mui/GridFilter/GridFilter.jsx
  • src/components/mui/GridFilter/components/Filter.jsx
  • src/components/mui/GridFilter/components/ValueInput/AsyncSelectInput.jsx
  • src/components/mui/GridFilter/components/ValueInput/CompanySelectInput.jsx
  • src/components/mui/GridFilter/components/ValueInput/DateTimeInput.jsx
  • src/components/mui/GridFilter/components/ValueInput/NumberInput.jsx
  • src/components/mui/GridFilter/components/ValueInput/SpeakerSelectInput.jsx
  • src/components/mui/GridFilter/components/ValueInput/index.jsx
  • src/components/mui/GridFilter/readme.md
  • src/components/mui/GridFilter/utils.js
  • src/components/mui/__tests__/GridFilter.test.jsx
  • src/i18n/en.json

Comment on lines +56 to +74
const fetchOptions = (searchTerm) => {
if (searchTerm && searchTerm.length < minSearchLength) {
setOptions([]);
return;
}
setLoading(true);
queryFunction(searchTerm, (rawResults) => {
setOptions((rawResults || []).map((item) => ({ ...formatOption(item), raw: item })));
setLoading(false);
});
};

useEffect(() => {
fetchOptions("");
return () => {
if (debounceRef.current) clearTimeout(debounceRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard against stale/out-of-order async responses when typing quickly.

fetchOptions accepts every callback result, so slower earlier requests can overwrite newer results, and callbacks can still update state after unmount. Track a request id + mounted flag so only the latest active request mutates state.

💡 Suggested fix
@@
   const [options, setOptions] = useState([]);
   const [loading, setLoading] = useState(false);
   const debounceRef = useRef(null);
+  const lastRequestIdRef = useRef(0);
+  const isMountedRef = useRef(true);
@@
   const fetchOptions = (searchTerm) => {
+    const requestId = ++lastRequestIdRef.current;
     if (searchTerm && searchTerm.length < minSearchLength) {
       setOptions([]);
+      setLoading(false);
       return;
     }
     setLoading(true);
     queryFunction(searchTerm, (rawResults) => {
+      if (!isMountedRef.current || requestId !== lastRequestIdRef.current) return;
       setOptions((rawResults || []).map((item) => ({ ...formatOption(item), raw: item })));
       setLoading(false);
     });
   };
@@
   useEffect(() => {
     fetchOptions("");
     return () => {
+      isMountedRef.current = false;
       if (debounceRef.current) clearTimeout(debounceRef.current);
     };

Also applies to: 76-80

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/mui/GridFilter/components/ValueInput/AsyncSelectInput.jsx`
around lines 56 - 74, The fetchOptions function has a race condition where
slower earlier requests can overwrite newer results when the user types quickly,
and callbacks can also update state after unmount. Add a ref to track the
mounted status and another ref to track the current request ID. In the
useEffect, set mounted to true and clear it in the cleanup function. Modify
fetchOptions to generate a unique request ID for each call and pass it to the
queryFunction callback. In the callback, before calling setOptions and
setLoading, check that the request ID matches the current request ID stored in
the ref and that the component is still mounted. This ensures only the latest
active request mutates state and prevents updates after unmount.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Line 3: The `@mui/x-date-pickers` package is currently listed only in
devDependencies but needs to be added to peerDependencies as well, since the new
DateTimeInput component (which uses DateTimePicker from this package) is
exported as part of the GridFilter component library. Add `@mui/x-date-pickers` to
the peerDependencies section in package.json with the same version constraint as
what is currently specified in devDependencies, so that consuming applications
receive clear guidance about this required dependency when using GridFilter with
the datetime value type.

In `@src/components/mui/GridFilter/hooks/useGridFilter.jsx`:
- Around line 29-30: The setFilters function on line 29-30 accepts any
joinOperator value without validation before dispatching to saveFilters, which
allows invalid values to be persisted in state and cause silent semantic drift
in the filter-reducer. Add validation logic inside setFilters to ensure the
joinOperator parameter is clamped to only valid values from JOIN_OPERATORS
(specifically ALL or ANY) before passing it to the dispatch call, using a safe
default value if an invalid value is provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2cbf0bec-fe2e-4eb4-9eb6-95aa3c0796d9

📥 Commits

Reviewing files that changed from the base of the PR and between df4fa23 and 75760ee.

📒 Files selected for processing (4)
  • package.json
  • src/components/mui/GridFilter/hooks/useGridFilter.jsx
  • src/components/mui/GridFilter/readme.md
  • src/components/mui/__tests__/useGridFilter.test.jsx

Comment thread package.json
{
"name": "openstack-uicore-foundation",
"version": "5.0.34",
"version": "5.0.36-beta.1",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing @mui/x-date-pickers in peerDependencies.

The new DateTimeInput component uses DateTimePicker from @mui/x-date-pickers, but this package is only listed in devDependencies (line 30), not in peerDependencies. Since this is a component library that exports GridFilter to consuming applications, @mui/x-date-pickers must be declared as a peer dependency.

Without this declaration, consuming applications that use GridFilter with the new datetime value type will encounter runtime errors when DateTimeInput attempts to import DateTimePicker, and developers won't receive clear guidance from package.json about the missing dependency.

📦 Proposed fix to add missing peer dependency
     "peerDependencies": {
         "`@emotion/react`": "^11.11.4",
         "`@emotion/styled`": "^11.11.5",
         "`@mui/icons-material`": "^6.4.3",
         "`@mui/material`": "^6.4.3",
+        "`@mui/x-date-pickers`": "^7.26.0",
         "`@react-pdf/renderer`": "^3.1.11",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 3, The `@mui/x-date-pickers` package is currently listed
only in devDependencies but needs to be added to peerDependencies as well, since
the new DateTimeInput component (which uses DateTimePicker from this package) is
exported as part of the GridFilter component library. Add `@mui/x-date-pickers` to
the peerDependencies section in package.json with the same version constraint as
what is currently specified in devDependencies, so that consuming applications
receive clear guidance about this required dependency when using GridFilter with
the datetime value type.

Comment on lines +29 to +30
const setFilters = (filters = [], joinOperator = JOIN_OPERATORS.ALL) =>
dispatch(saveFilters(id, filters, joinOperator));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate joinOperator before dispatching.

Line 29 forwards any joinOperator value into state; an invalid value is persisted and skips the JOIN_OPERATORS.ANY path in filter-reducer, so parsed semantics can silently drift. Clamp to ALL | ANY at this API boundary.

Suggested patch
-  const setFilters = (filters = [], joinOperator = JOIN_OPERATORS.ALL) =>
-    dispatch(saveFilters(id, filters, joinOperator));
+  const setFilters = (filters = [], joinOperator = JOIN_OPERATORS.ALL) => {
+    const safeJoinOperator =
+      joinOperator === JOIN_OPERATORS.ANY
+        ? JOIN_OPERATORS.ANY
+        : JOIN_OPERATORS.ALL;
+    dispatch(saveFilters(id, filters, safeJoinOperator));
+  };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/mui/GridFilter/hooks/useGridFilter.jsx` around lines 29 - 30,
The setFilters function on line 29-30 accepts any joinOperator value without
validation before dispatching to saveFilters, which allows invalid values to be
persisted in state and cause silent semantic drift in the filter-reducer. Add
validation logic inside setFilters to ensure the joinOperator parameter is
clamped to only valid values from JOIN_OPERATORS (specifically ALL or ANY)
before passing it to the dispatch call, using a safe default value if an invalid
value is provided.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant